package futbol.tacticas.concursantes.qualitycode.ctrlteam;

import java.awt.Point;
import java.util.ArrayList;
import java.util.List;

import futbol.General;
import futbol.tacticas.concursantes.qualitycode.QCodeTactica;
import futbol.tacticas.concursantes.qualitycode.common.QBall;
import futbol.tacticas.concursantes.qualitycode.common.QPlayer;
import futbol.tacticas.concursantes.qualitycode.common.QUtils;
import futbol.tacticas.concursantes.qualitycode.opponent.QOpponentPlayer;

public class QPases
{
  public static enum TipoPase { AUTO_ADELANTE, AUTO_ATRAS,
                                ADELANTE, ATRAS,
                                LARGO_ADELANTE, LARGO_ATRAS,
                                ANY, LARGO_ANY;
                                };
  
  public static final int MIN_DIST = 20;
  public static final int LARGO_DIST = 70;
  
  private static final int offset = 500;
  public static final List<Point> AUTO_ADELANTE = new ArrayList<Point>(3);
  public static final List<Point> AUTO_ATRAS = new ArrayList<Point>(5);
  
  static
  {
    AUTO_ADELANTE.add(new Point(offset,0));
    AUTO_ADELANTE.add(new Point(offset,offset/2));
    AUTO_ADELANTE.add(new Point(offset,-offset/2));
    AUTO_ADELANTE.add(new Point(offset,offset));    
    AUTO_ADELANTE.add(new Point(offset/2,offset));
    AUTO_ADELANTE.add(new Point(offset/2,offset));    
    AUTO_ADELANTE.add(new Point(offset,-offset));
    AUTO_ADELANTE.add(new Point(offset/2,-offset));
    
    AUTO_ATRAS.add(new Point(-offset,0));
    AUTO_ATRAS.add(new Point(      0,-offset));
    AUTO_ATRAS.add(new Point(-offset,-offset));    
    AUTO_ATRAS.add(new Point(-offset,offset));
    AUTO_ATRAS.add(new Point(      0,offset));
  }
  
  protected final QCtrlPlayer controlador;
  protected final QCtrlTeam team;
  
  protected final List<Pase> pasesCalculados = new ArrayList<Pase>();
  
  public QPases(QCtrlPlayer controlador, QCtrlTeam team)
  {
    this.controlador = controlador;
    this.team = team;
  }
  
  private boolean isLargo(TipoPase tipoPase)
  {
    return  tipoPase == TipoPase.LARGO_ADELANTE || 
            tipoPase == TipoPase.LARGO_ATRAS || 
            tipoPase == TipoPase.LARGO_ANY;
  }
  
  public boolean isAutoPase(TipoPase tipoPase)
  {
    return  tipoPase == TipoPase.AUTO_ADELANTE || 
            tipoPase == TipoPase.AUTO_ATRAS; 
  }
  
  public void reset()
  {
    pasesCalculados.clear();
  }
  
  private Pase estaCalculado(TipoPase tipoPase)
  {
    for (Pase pase : pasesCalculados)
    {
      if (pase.tipoPase == tipoPase)
        return pase;
    }
    return null;
  }
  
  public QPlayer pase(QCtrlTeam team, TipoPase tipoPase)
  {
    Pase pase = estaCalculado(tipoPase);
    if (pase == null)
    {
      if (tipoPase == TipoPase.AUTO_ADELANTE || tipoPase == TipoPase.AUTO_ATRAS)
        pase = new AutoPase();
      else
        pase = new PaseOtroJugador();
    } 
    return pase.hasPase(team, tipoPase); 
  }

  private void filtrarXDistancia(QPlayer player, List<? extends QPlayer> players, int distanciaMinima)
  {
    for (int i = players.size()-1; i >= 0; i--)
    {
      final QPlayer a = players.get(i);
      final int dis = (int) General.getDistancia(player.getX(), player.getY(), a.getX(), a.getY());
      if (dis <= distanciaMinima)
        players.remove(i);
    }
  }  
  
  public boolean hasPase(QPlayer player)
  {
    return hasPase(player, player.getX(), player.getY());
  }

  public boolean hasPase(QPlayer pDestino, int xBallDest, int yBallDest)
  {
    final QBall ball = new QBall(team.getBall());
    ball.golpearBola(controlador, xBallDest, yBallDest);
    final List<QBall> fBalls = ball.getFutureBalls();
    if (!canGolpearOpponentBefore(pDestino, fBalls))
      return true;
    
    return false;
  }
  
  private boolean canGolpearOpponentBefore(QPlayer player, List<QBall> balls)
  {
    final int playerIdx = player.turnosInterseptarBola(balls)+1;
    for(QOpponentPlayer opponent: team.getOpponentTeam().getPlayers())
    {
      final int opponentIdx = opponent.turnosInterseptarBola(balls);        
      if (opponentIdx > 0 && opponentIdx <= playerIdx)
        return true;
    }    
    return false;
  }

  
//---------------------------------------------------------------- Algoritmos para pases  
  
  private class Pase
  {
    protected QPlayer recibidor = null;
    protected TipoPase tipoPase = null;
    
    public void reset()
    {
      tipoPase = null;      
    }
    
    public TipoPase getTipoPase()
    {
      return tipoPase;
    }
    
    public QPlayer hasPase(QCtrlTeam team, TipoPase tipoPase)
    {
      if (this.tipoPase == null)
      {
        this.tipoPase = tipoPase;
        calcular(team, tipoPase);
      }
      return recibidor;
    }
    
    protected void calcular(QCtrlTeam team, TipoPase tipoPase)
    {}
  }
  
  public class PaseOtroJugador extends Pase
  {
    protected List<QCtrlPlayer> filtroXTipoPase(TipoPase tipoPase)
    {
      List<QCtrlPlayer> players = team.cercania(controlador);
      filtrarXDistancia(controlador, players, MIN_DIST);
            
      if (tipoPase == TipoPase.ADELANTE || tipoPase == TipoPase.LARGO_ADELANTE)
        players = QUtils.filtrar(players, controlador.getPos(), QUtils.Filter.ADELANTE);
      else if (tipoPase == TipoPase.ATRAS || tipoPase == TipoPase.LARGO_ATRAS)
        players = QUtils.filtrar(players, controlador.getPos(), QUtils.Filter.ATRAS);
      
      if (isLargo(tipoPase))
        players = QUtils.filtrar(players, controlador.getPos(), LARGO_DIST, false);

      return players;
    }
    
    protected void calcular(QCtrlTeam team,TipoPase tipoPase)
    {
      final List<QCtrlPlayer> filtrados = filtroXTipoPase(tipoPase); 
      calcular(filtrados);
    }
    
    protected void calcular(List<QCtrlPlayer> players)
    {      
      if (players.size() > 0)
      {       
        for (QCtrlPlayer p : players)
        {
          QPlayer player = p.futurePos(team.getBall().getPos(), 1);
          if ((controlador.getForce()*3) <= QBall.KICK_ABLE_SPEED || isLargo(tipoPase))
          {
            if (QPases.this.hasPase(player))
            {
              recibidor = player;
              break;
            }
          }
          else
          {
            double dis = General.getDistancia(controlador.getX(), controlador.getY(), player.getX(), player.getY());
            if (dis < QBall.DISTANCIA_MAXFUERZA)
            {
              if (QPases.this.hasPase(player))
              {
                recibidor = player;
                break;
              }              
            }
            else
            {
              final Point p40 = QUtils.extraInterPolate(controlador.getPos(), player.getPos(), QBall.DISTANCIA_MAXFUERZA-10);
              if (QPases.this.hasPase(player,p40.x, p40.y))
              {
                recibidor = new QPlayer(player);
                recibidor.updatePos(p40);
                break;
              }
            }
          }
        }
      }
    }    
  }

  public class AutoPase extends Pase
  {
    protected void calcular(QCtrlTeam team, TipoPase tipoPase)
    {
      if (tipoPase == TipoPase.AUTO_ADELANTE)
      {
        if (!enDireccionAMarco())
          calcular(team, AUTO_ADELANTE);
      }
      else
        calcular(team, AUTO_ATRAS);
    }
    
    protected boolean enDireccionAMarco()
    {
      final List<? extends QPlayer> opponents = team.getOpponentTeam().cercania(controlador);
      final double dist = opponents.get(0).distance(controlador) - QBall.KICK_RADIO;
      final Point marco = new Point(160,0);
      
      if (dist < 15)
      {
        //return exito(team, opponents, marco, false);
        return false;
      }
      else
      {
        calcularTranquilo(marco);
        return true;
      }
    }

    protected void calcular(QCtrlTeam team, List<Point> points)
    {  
      final List<? extends QPlayer> opponents = team.getOpponentTeam().cercania(controlador);
      final double dist = opponents.get(0).distance(controlador) - QBall.KICK_RADIO;
      
      if (team.portero() == controlador && dist < 30 || dist < 24)
      {
        /*for (Point point : points)
          if (exito(team, opponents, point, true))
            break;/**/
      }
      else
      {
        calcularTranquilo(points.get(0));
      } 
    }
    
    private void calcularTranquilo(Point p)
    {
      if (controlador.getX() > 50 && controlador.getForce() < 6)
        return;
      
      if (controlador.getForce() == QCodeTactica.FORCE_7_MAX)
        recibidor = controlador.futurePos(p,3);
      else if (controlador.getForce() == QCodeTactica.FORCE_5)
        recibidor = controlador.futurePos(p,5);
      else
        recibidor = controlador.futurePos(p,3);/**/
    }

    private boolean exito(QCtrlTeam team, final List<? extends QPlayer> opponents, Point point, boolean mover)
    {
      final Point p = new Point(point);
      if (mover)
        p.translate(controlador.getX(), controlador.getY());
     
      final QPlayer fPlayer = controlador.futurePos(p,1);      
      final QBall fBall = new QBall(team.getBall());
      int turnos = 1;
      for (; turnos < 10; turnos++)
      {
        fBall.golpearBola(controlador, fPlayer.getX(), fPlayer.getY());
        if (fBall.getFutureBalls().size() > 2)
          break;
        fPlayer.move();
      }
      
      final QPlayer fOpponent = opponents.get(0).futurePos(team.getBall().getPos(), 1);
      final List<QBall> balls = fBall.getFutureBalls();
      if (fOpponent.turnosInterseptarBola(balls) > 1)
      {
        recibidor = fPlayer;
        return true;
      }
      return false;      
    }
  }
}
